#include "FunctionCall.h"
#include "../Exceptions.h"
#include "../GeneralVisitor.h"
#include "../Constants.h"

FunctionCall::FunctionCall(string iFunctionName, vector<TreeNode*> iParameters)
{
    this->fFunctionName = iFunctionName;
    this->fParameters = iParameters;
    this->fDefinition = 0;
}

FunctionCall::~FunctionCall()
{
    for(vector<TreeNode*>::iterator it = fParameters.begin(); it != fParameters.end(); ++it)
    {
        delete (*it);
    }

    fParameters.clear();

    // fDefinition nemazeme, my mame jenom pointer, maze se to v ParseHelper
}

void FunctionCall::CheckVariables(set<string>& oVariables, string iParentName)
{
    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckVariables(oVariables, iParentName);
    }
}

void FunctionCall::CheckFunctionCalls(set<FunctionDefinition*>& iAllFunctions, string iParentName)
{
    vector<FunctionDefinition*> lFunctionsWithSameName;
    set<FunctionDefinition*>::iterator lIterator;
    set<FunctionDefinition*>::iterator lEnd = iAllFunctions.end();

    for(lIterator = iAllFunctions.begin(); lIterator != lEnd; ++lIterator)
    {
        if((*lIterator)->GetName() == this->fFunctionName)
        {
            lFunctionsWithSameName.push_back(*lIterator);
        }
    }

    // hups... volame neexistujici funkci...
    // predpokladejme ze to je nejaka zabudovana funkce; on-the-fly ji tady pridame do seznamu
    if(lFunctionsWithSameName.size() == 0)
    {
        FunctionDefinition* lDef = Util::BuildPredefinedFunction(this->fFunctionName, this->fParameters.size());
        iAllFunctions.insert(lDef);

        // toto je takovy fake, abych nemusel upravovat moc kodu... doky tomu projde kontrola na pocet parametru
        // a ja si tu funkci pak priradim do fDefinition
        lFunctionsWithSameName.push_back(lDef);
    }

    for(vector<FunctionDefinition*>::iterator it = lFunctionsWithSameName.begin(); it != lFunctionsWithSameName.end(); ++it)
    {
        if((*it)->GetParamCount() == this->fParameters.size())
        {
            this->fDefinition = *it;
            break;
        }
    }

    // funkce s timto jmenem existuje, ale neprijima pozadovany pocet parametru
    // taky tady predstirejme, ze je to nejaka preddefinovana funkce
    if(this->fDefinition == 0)
    {
        FunctionDefinition* lDef = Util::BuildPredefinedFunction(this->fFunctionName, this->fParameters.size());
        iAllFunctions.insert(lDef);
        this->fDefinition = lDef;
    }

    if(this->fParameters.size() > MAX_FUNCTION_CALL_PARAMS)
        throw TooMuchCallParamCount(iParentName, fFunctionName, MAX_FUNCTION_CALL_PARAMS, fParameters.size());

    // a jeste rekurzivne na parametry - tam se taky muze volat funkce
    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckFunctionCalls(iAllFunctions, iParentName);
    }
}

void FunctionCall::CheckClassConstructions(set<ClassDefinition*>& iAllClasses, string iParentName)
{
    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckClassConstructions(iAllClasses, iParentName);
    }
}

void FunctionCall::CheckExpressionTypes(map<string, ExpressionType*>& iVarTypes, string iParentName)
{
    for(vector<TreeNode*>::iterator it = this->fParameters.begin(); it != this->fParameters.end(); ++it)
    {
        (*it)->CheckExpressionTypes(iVarTypes, iParentName);
    }

    // pokud je to built-in funkce, zkontroluj typy parametru... napr. array("xxx") neni uplne ono :-)
    if(fDefinition != 0 && fDefinition->IsBuiltIn())
        fDefinition->CheckBuiltInFunctionCallParams(iVarTypes, this->fParameters);
}

ExpressionType* FunctionCall::GetType(map<string, ExpressionType*>& iVarTypes, string iParentName)
{
    // CheckExpressionTypes se explicitne vola na uzly dedici od Statement*
    // a pokud je volani funkce pouzito jako Expression*, tak je to potreba zavolat tady
    this->CheckExpressionTypes(iVarTypes, iParentName);

    if(fDefinition == 0)
        return new ExpressionType(Unresolved);

    if(fDefinition->IsBuiltIn())
    {
        return fDefinition->GetBuiltInFunctionReturnType();
    }
    else
    {
        ReturnStatement* lReturn = fDefinition->GetReturnStatement();

        if(lReturn == 0) // zadny return tam neni
        {
            throw CallWithoutReturnException(iParentName, fFunctionName, true);
        }

        return new ExpressionType(Unresolved);
    }
}

void FunctionCall::Accept(GeneralVisitor* iVisitor)
{
    iVisitor->Visit(this);
}
